#!/bin/sh

# TODO: Guix pull on commit specified in package-lists/guix-version?
# TODO: On foreign distro, use local package manager to install Guix.  (Start with pacman.)
# TODO: Make functions to make it easier to re-order calls?

HTTPS_ROOT=https://gitlab.com/
SSH_ROOT=git@gitlab.com:
ROOT=$HTTPS_ROOT
PROFILE=/tmp/homeinit-$USER/homeinit
USER_NAME=Ambrevar
SUBSTITUTE_URLS=https://ci.guix.gnu.org
EXTRA_SUBSTITUTE_URLS=
## Hardcoded in .mbsyncrc:
MAIL_CACHE="$HOME/.cache/mail"

[ -z "$SOURCEDIR" ] && SOURCEDIR="$PERSONAL"
[ -z "$XDG_CONFIG_HOME" ] && XDG_CONFIG_HOME="$HOME/.config"
[ -z "$XDG_DATA_HOME" ] && XDG_DATA_HOME="$HOME/.local/share"
[ -z "$XDG_BIN_HOME" ] && XDG_BIN_HOME="$XDG_DATA_HOME/../bin"

usage() {
	cat <<EOF>&2
Usage: ${0##*/}

Initialize user profile: install packages, set up folders, etc.

Options:

  -g DEVICE: Device where to sync ~/.gnupg from, e.g. '/dev/sda1'.
  -u:        Skip large updates (large packages, email cache, etc.)
  -s URLS:   Extra substitute URLs for Guix (space separated).
             Example: 'http://192.168.1.2:8080'.

Environment variables:

  SOURCEDIR=$SOURCEDIR
  XDG_CONFIG_HOME=$XDG_CONFIG_HOME
  XDG_DATA_HOME=$XDG_DATA_HOME
  XDG_BIN_HOME=$XDG_BIN_HOME

Tips:

- Run the following command to install the Guix corresponding to your substitute
  server:

    guix pull -C my-channels.scm

- Copy the email cache to '$MAIL_CACHE' to speed up the process.

Direct link:

  ${HTTPS_ROOT}ambrevar/dotfiles/raw/master/.local/bin/homeinit
EOF
	exit
}

OPT_UPDATE=true
OPT_DEVICE=""
while getopts ":hg:s:u" opt; do
	case $opt in
	h)
		usage
		exit ;;
	g)
		OPT_DEVICE="$OPTARG" ;;
	s)
		EXTRA_SUBSTITUTE_URLS="$OPTARG $EXTRA_SUBSTITUTE_URLS" ;;
	u)
		OPT_UPDATE=false ;;
	\?)
		usage
		exit 1 ;;
	esac
done

shift $(($OPTIND - 1))

RED='\033[0;31m\033[1m'
GREEN='\033[0;32m\033[1m'
YELLOW='\033[0;33m\033[1m'
BOLD='\033[0m\033[1m'
NORMAL='\033[0m'

section() {
	echo -e "$GREEN==> $@$NORMAL"
}
message() {
	echo -e "$YELLOW:: $@$NORMAL"
}
info() {
	echo -e "$@"
}
warning() {
	echo -e "${YELLOW}WARNING:: $@$NORMAL"
}

## "ln" wrapper.
## If $2 is a folder, create the link in it using the basename of $1.
## Existing files are no clobbered, unless they already are a symlink.
lnn() { # $1=TARGET $2=LINK|FOLDER
	if [ -d "$2" ]; then
		set -- "$1" "$2/$(basename "$1")"
	fi
	if [ ! -e "$2" ] || [ -h "$2" ]; then
		ln -svnf "$1" "$2"
	fi
}

inpath() {
	for i; do
		if ! command -v "$i" >/dev/null 2>&1; then
			return 1
		fi
	done
	return 0
}

## Pass substitute URLs dynamically to avoid overriding the daemon settings.
run_guix() {
	if [ -z "$EXTRA_SUBSTITUTE_URLS" ]; then
		guix "$@"
	else
		guix --substitute-urls="$EXTRA_SUBSTITUTE_URLS $SUBSTITUTE_URLS" "$@"
	fi
}
run_current_guix() {
	if [ -z "$EXTRA_SUBSTITUTE_URLS" ]; then
		~/.config/guix/current/bin/guix "$@"
	else
		~/.config/guix/current/bin/guix --substitute-urls="$EXTRA_SUBSTITUTE_URLS $SUBSTITUTE_URLS" "$@"
	fi
}

################################################################################
#

section "Initial packages"
if inpath guix; then
	mkdir -p ~/.config/guix

	## OpenSSH is not an input of Git, upstream does not want to increase the
	## closure size that much.
	mkdir -p "$(dirname "$PROFILE")"
	run_guix package --profile="$PROFILE" --install openssh gnupg git stow password-store pinentry-tty pinentry-gtk2 cryptsetup recutils
	source "$PROFILE"/etc/profile
fi

has_gpg_keys() {
	[ -n "$(ls -1 ~/.gnupg/private-*)" ]
}

is_laptop() {
	[ -n "$(ls -1 /sys/class/power_supply/ 2>/dev/null)" ]
}

if ! has_gpg_keys; then
	section "GPG sync"
	if [ -n "$OPT_DEVICE" ]; then
		section "GnuPG"
		sudo cryptsetup open "$OPT_DEVICE" gpg_backup
		sudo mount -o compress=zstd /dev/mapper/gpg_backup /mnt
		cp -a /mnt/public/.gnupg ~/
		sudo umount /mnt
		sudo cryptsetup close gpg_backup
	else
		warning "No device specified."
	fi
fi

section "GPG"
if has_gpg_keys; then
	ROOT=$SSH_ROOT
	## Set up gpg-agent to authenticate to SSH_ROOT.
	chmod -R go-rwx ~/.gnupg
	export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
	if [ "$GPG_TTY" != "not a tty" ] || [ -z "$INSIDE_EMACS" ]; then
		## If a TTY, since our ~/.gnupg/gpg-agent.conf exists and specifies the Emacs
		## pinentry, we must force the TTY version or else it won't work from a TTY.
		## Same if not in Emacs (e.g. Xterm).
		gpgconf --kill gpg-agent
		cat<<EOF>"$(dirname "$PROFILE")/gpg-agent.conf"
## 1-day timeout
default-cache-ttl 86400
max-cache-ttl 86400
## SSH
enable-ssh-support
default-cache-ttl-ssh 86400
max-cache-ttl-ssh 86400
## Force pinentry (should be pinentry-tty)
pinentry-program $(readlink -f $(which pinentry))
EOF
		gpg-agent --homedir ~/.gnupg --daemon --options "$(dirname "$PROFILE")/gpg-agent.conf"
	fi
	## Start gpg-agent manually since SSH requests do not do it automatically.
	gpg-connect-agent updatestartuptty /bye

	if [ -e "$SOURCEDIR" ]; then
		git -C "$SOURCEDIR" pull
	else
		git clone ${SSH_ROOT}$USER_NAME/personal "$SOURCEDIR"
	fi
	if [ -e ~/.password-store ]; then
		git -C ~/.password-store pull
	else
		git clone ${SSH_ROOT}$USER_NAME/password-store ~/.password-store
		## The following is necessary to make sure the 'diff' GPG filter is properly set up.
		pass git init
	fi
else
	warning "~/.gnupg not found."
fi

section "Persistent folders"
for i in "$MAIL_CACHE" ~/.config ~/.config/guix ~/.config/guix-gaming-channels ~/.config/transmission-daemon ~/.emacs.d "$XDG_DATA_HOME" ~/.mpv ~/projects; do
	mkdir -pv "$i"
done

section "Gaming"
lnn "$SOURCEDIR/games/games.scm.gpg" ~/.config/guix-gaming-channels/

section "dotfiles"
if [ -e ~/dotfiles ]; then
	git -C ~/dotfiles remote set-url origin ${ROOT}$USER_NAME/dotfiles
	git -C ~/dotfiles pull
else
	git clone ${ROOT}$USER_NAME/dotfiles ~/dotfiles || exit 1
fi
pushd ~/dotfiles
## .bash_profile may prevent .profile from being parsed, so we move it.
for i in ~/.bash_profile ~/.bashrc; do
	[ -e "$i" ] && mv -v "$i" "$i".old
done
stow -v . || exit 1
popd

guix_install_profile() {
	local profile
	local manifest
	manifest=$HOME/.package-lists/guix-$1-manifest.scm
	if [ -f "$manifest" ]; then
		if [ "$1" = "default" ]; then
			info "Installing default profile..."
			run_current_guix package --manifest="$manifest" --keep-failed
			. ~/.guix-profile/etc/profile
		else
			profile=$HOME/.guix-extra-profiles/$1/$1
			if [ ! -d "$HOME/.guix-extra-profiles/$1/$1" ]; then
				info "Installing profile '$profile'..."
				mkdir -p $(dirname "$profile")
				run_current_guix package --manifest="$manifest" --keep-failed --profile="$profile"
				if [ -f "$profile"/etc/profile ]; then
					. "$profile"/etc/profile
				fi
			else
				info "Profile '$profile' already installed."
			fi
		fi
	else
		warning "Manifest '$manifest' not found."
	fi
}

section "System packages"
if inpath guix; then
	if [ ! -e ~/.cache/guix ]; then
		message "First 'guix pull'"
		run_guix pull
		hash guix
	fi

	for i in default main emacs nyxt texlive; do
		guix_install_profile "$i"
	done
	if is_laptop; then
		guix_install_profile laptop
		guix_install_profile laptop-gaming
	else
		guix_install_profile gaming
	fi
	if $OPT_UPDATE; then
		for i in blender chromium electrum racket; do
			guix_install_profile "$i"
		done
	fi
else
	warning "Package manager not supported.  Install Guix."
fi

if inpath emacs; then
	message "Emacs cache folder"
	mkdir -pv "$HOME/.cache/emacs/"
	message "Emacs local packages"
	if [ -e "$XDG_DATA_HOME"/emacs/site-lisp ]; then
		for i in "$XDG_DATA_HOME"/emacs/site-lisp/*; do
			echo "$i"
			git -C "$i" pull
		done
	else
		mkdir -pv "$XDG_DATA_HOME"/emacs/site-lisp
	fi
fi

if inpath guix; then
	section "Cleanup initial packages"
	rm -rv "$(dirname "$PROFILE")"
fi

if [ -x "$XDG_BIN_HOME"/updatedb-local ]; then
	section "locate db"
	"$XDG_BIN_HOME"/updatedb-local
fi

section "Mail"
# lnn "$SOURCEDIR/mail/authinfo.gpg" "$HOME/.authinfo.gpg" ## Only if not using password-store.
mkdir -pv "$MAIL_CACHE"
while IFS= read -r i; do
	## Warning: We need to eval here to expand the "~".
	mkdir -pv $(eval echo $i)
done <<EOF
$(awk '/^Path/ {print $2}' ~/.mbsyncrc)
EOF

if $OPT_UPDATE; then
	mbsync -aV
	# mu index --maildir="$MAIL_CACHE"
	notmuch new
fi
